﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
using ICodeShare.UI.Converters.Helpers;

namespace ICodeShare.UI.Converters
{
    #region IValueConverter
    /// <summary>
    /// 类型转换器，在设定的两种类型间进行转换
    /// </summary>
    /// <example>
    /// 1.在xmal文件引用DateConverter类所在命名空间。
    ///   xmlns:cvts="http://schemas.extended.wpf.com/converters"
    /// 2.在xaml文件添加Resources。
    ///   <Window.Resources>
    ///   <cvts:ObjectTypeConverter x:Key="cvtsObjectType"/>
    ///   </Window.Resources>
    /// 3.在xaml文件中指定Binding值的Converter
    ///   <TextBox Text="{Binding ShowTheText,Converter={StaticResource cvtsObjectType}}"></TextBox>
    /// </example>
    [ValueConversion(typeof(object), typeof(object))]
    public sealed class ObjectTypeConverter : IValueConverter
    {
        private static readonly ExceptionHelper exceptionHelper = new ExceptionHelper(typeof(ObjectTypeConverter));
        private Type _sourceType;
        private Type _targetType;

        /// <summary>
        /// Initializes a new instance of the ObjectTypeConverter class.
        /// </summary>
        public ObjectTypeConverter()
        {
        }

        /// <summary>
        /// Initializes a new instance of the ObjectTypeConverter class with the specified source and target types.
        /// </summary>
        /// <param name="sourceType">
        /// The source type (see <see cref="SourceType"/>).
        /// </param>
        /// <param name="targetType">
        /// The target type (see <see cref="TargetType"/>).
        /// </param>
        public ObjectTypeConverter(Type sourceType, Type targetType)
        {
            this.SourceType = sourceType;
            this.TargetType = targetType;
        }

        /// <summary>
        /// Gets or sets the source type for the conversion.
        /// </summary>
        [ConstructorArgument("sourceType")]
        public Type SourceType
        {
            get { return this._sourceType; }
            set { this._sourceType = value; }
        }

        /// <summary>
        /// Gets or sets the target type for the conversion.
        /// </summary>
        [ConstructorArgument("targetType")]
        public Type TargetType
        {
            get { return this._targetType; }
            set { this._targetType = value; }
        }

        /// <summary>
        /// Attempts to convert the specified value.
        /// </summary>
        /// <param name="value">
        /// The value to convert.
        /// </param>
        /// <param name="targetType">
        /// The type of the binding target property.
        /// </param>
        /// <param name="parameter">
        /// The converter parameter to use.
        /// </param>
        /// <param name="culture">
        /// The culture to use in the converter.
        /// </param>
        /// <returns>
        /// A converted value.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            exceptionHelper.ResolveAndThrowIf(this.TargetType == null, "NoTargetType");
            return DoConversion(value, this.TargetType, culture);
        }

        /// <summary>
        /// Attempts to convert the specified value back.
        /// </summary>
        /// <param name="value">
        /// The value to convert.
        /// </param>
        /// <param name="targetType">
        /// The type of the binding target property.
        /// </param>
        /// <param name="parameter">
        /// The converter parameter to use.
        /// </param>
        /// <param name="culture">
        /// The culture to use in the converter.
        /// </param>
        /// <returns>
        /// A converted value.
        /// </returns>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            exceptionHelper.ResolveAndThrowIf(this.SourceType == null, "NoSourceType");
            return DoConversion(value, this.SourceType, culture);
        }

        private static object DoConversion(object value, Type toType, CultureInfo culture)
        {
            if ((value is IConvertible) || (value == null))
            {
                try
                {
                    return System.Convert.ChangeType(value, toType, culture);
                }
                catch (Exception)
                {
                    return DependencyProperty.UnsetValue;
                }
            }
            else
            {
                var typeConverter = TypeDescriptor.GetConverter(value);

                if (typeConverter.CanConvertTo(toType))
                {
                    return typeConverter.ConvertTo(null, culture, value, toType);
                }
            }

            return DependencyProperty.UnsetValue;
        }
    }
    #endregion
}
